{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ec0a3905",
   "metadata": {},
   "source": [
    "### 代码实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0f6d4f48",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入必要的库\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torchinfo import summary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c3477e3",
   "metadata": {},
   "source": [
    "### 基础卷积结构定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "40db945b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义一个基础卷积结构BasicConv2d，改进型\n",
    "class BasicConv2d(nn.Module):\n",
    "    def __init__(self, in_channels, out_channels, **kwargs):\n",
    "        super().__init__()\n",
    "        # 卷积+BN层\n",
    "        self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)\n",
    "        self.bn = nn.BatchNorm2d(out_channels, eps=0.001)\n",
    "\n",
    "    # 定义前向传播函数\n",
    "    def forward(self, x):\n",
    "        # 依次经过卷积和BN层，最后激活函数ReLU\n",
    "        x = self.conv(x)\n",
    "        x = self.bn(x)\n",
    "        return F.relu(x, inplace=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aef05efc",
   "metadata": {},
   "source": [
    "### Inception结构定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fb415deb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义Inception结构\n",
    "class Inception(nn.Module):\n",
    "\n",
    "    # in_channels表示上一层输入的通道数，ch1x1表示1x1卷积的个数\n",
    "    # ch3x3red表示3x3卷积之前1x1卷积的个数，ch3x3表示3x3卷积的个数\n",
    "    # ch5x5red表示5x5卷积之前1x1卷积的个数，ch5x5表示5x5卷积的个数\n",
    "    # pool_proj表示池化后1x1卷积的个数\n",
    "    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch5x5red, ch5x5, pool_proj):\n",
    "        super().__init__()\n",
    "\n",
    "        # 定义四个分支路径\n",
    "        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)\n",
    "        self.branch2 = nn.Sequential(\n",
    "            BasicConv2d(in_channels, ch3x3red, kernel_size=1),\n",
    "            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)\n",
    "        )\n",
    "        self.branch3 = nn.Sequential(\n",
    "            BasicConv2d(in_channels, ch5x5red, kernel_size=1),\n",
    "            BasicConv2d(ch5x5red, ch5x5, kernel_size=5, padding=2)\n",
    "        )\n",
    "        self.branch4 = nn.Sequential(\n",
    "            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),\n",
    "            BasicConv2d(in_channels, pool_proj, kernel_size=1)\n",
    "        )\n",
    "\n",
    "    # 定义前向传播函数\n",
    "    def forward(self, x):\n",
    "        # 经过四个分支路径\n",
    "        branch1 = self.branch1(x)\n",
    "        branch2 = self.branch2(x)\n",
    "        branch3 = self.branch3(x)\n",
    "        branch4 = self.branch4(x)\n",
    "\n",
    "        # 连结结果后输出\n",
    "        outputs = [branch1, branch2, branch3, branch4]\n",
    "        return torch.cat(outputs, dim=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60da60ee",
   "metadata": {},
   "source": [
    "### 结构定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "d35ec9ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义GoogLeNet的网络结构\n",
    "class GoogLeNet(nn.Module):\n",
    "\n",
    "    def __init__(self, num_classes=1000):\n",
    "        super().__init__()\n",
    "        \n",
    "        # 第一部分，卷积+最大池化\n",
    "        self.part1 = nn.Sequential(\n",
    "            BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3),\n",
    "            nn.MaxPool2d(3, stride=2, ceil_mode=True)\n",
    "        )\n",
    "        # 第二部分，卷积+卷积+最大池化\n",
    "        self.part2 = nn.Sequential(\n",
    "            BasicConv2d(64, 64, kernel_size=1),\n",
    "            BasicConv2d(64, 192, kernel_size=3, padding=1),\n",
    "            nn.MaxPool2d(3, stride=2, ceil_mode=True)\n",
    "        )\n",
    "        # 第三部分，Inception*2 + 最大池化，数值参考论文结构表\n",
    "        self.part3 = nn.Sequential(\n",
    "            Inception(192, 64, 96, 128, 16, 32, 32),\n",
    "            Inception(256, 128, 128, 192, 32, 96, 64),\n",
    "            nn.MaxPool2d(3, stride=2, ceil_mode=True)\n",
    "        )\n",
    "        # 第四部分，Inception*5 + 最大池化，数值参考论文结构表\n",
    "        self.part4 = nn.Sequential(\n",
    "            Inception(480, 192, 96, 208, 16, 48, 64),\n",
    "            Inception(512, 160, 112, 224, 24, 64, 64),\n",
    "            Inception(512, 128, 128, 256, 24, 64, 64),\n",
    "            Inception(512, 112, 144, 288, 32, 64, 64),\n",
    "            Inception(528, 256, 160, 320, 32, 128, 128),\n",
    "            nn.MaxPool2d(3, stride=2, ceil_mode=True)\n",
    "        )\n",
    "        # 第五部分，Inception*2 + 平均池化，数值参考论文结构表\n",
    "        self.part5 = nn.Sequential(\n",
    "            Inception(832, 256, 160, 320, 32, 128, 128),\n",
    "            Inception(832, 384, 192, 384, 48, 128, 128),\n",
    "            nn.AdaptiveAvgPool2d((1, 1))\n",
    "        )\n",
    "        # 第六部分，Flatten+Dropout+全连接\n",
    "        self.part6 = nn.Sequential(\n",
    "            nn.Flatten(),\n",
    "            nn.Dropout(0.4),\n",
    "            nn.Linear(1024, num_classes)\n",
    "        )\n",
    "\n",
    "    # 定义前向传播函数\n",
    "    def forward(self, x):\n",
    "        # 依次经过六个部分后输出\n",
    "        x = self.part1(x)\n",
    "        x = self.part2(x)\n",
    "        x = self.part3(x)\n",
    "        x = self.part4(x)\n",
    "        x = self.part5(x)\n",
    "        x = self.part6(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1496b20",
   "metadata": {},
   "source": [
    "### 网络结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cf4a91af",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "===============================================================================================\n",
       "Layer (type:depth-idx)                        Output Shape              Param #\n",
       "===============================================================================================\n",
       "GoogLeNet                                     [1, 1000]                 --\n",
       "├─Sequential: 1-1                             [1, 64, 56, 56]           --\n",
       "│    └─BasicConv2d: 2-1                       [1, 64, 112, 112]         --\n",
       "│    │    └─Conv2d: 3-1                       [1, 64, 112, 112]         9,408\n",
       "│    │    └─BatchNorm2d: 3-2                  [1, 64, 112, 112]         128\n",
       "│    └─MaxPool2d: 2-2                         [1, 64, 56, 56]           --\n",
       "├─Sequential: 1-2                             [1, 192, 28, 28]          --\n",
       "│    └─BasicConv2d: 2-3                       [1, 64, 56, 56]           --\n",
       "│    │    └─Conv2d: 3-3                       [1, 64, 56, 56]           4,096\n",
       "│    │    └─BatchNorm2d: 3-4                  [1, 64, 56, 56]           128\n",
       "│    └─BasicConv2d: 2-4                       [1, 192, 56, 56]          --\n",
       "│    │    └─Conv2d: 3-5                       [1, 192, 56, 56]          110,592\n",
       "│    │    └─BatchNorm2d: 3-6                  [1, 192, 56, 56]          384\n",
       "│    └─MaxPool2d: 2-5                         [1, 192, 28, 28]          --\n",
       "├─Sequential: 1-3                             [1, 480, 14, 14]          --\n",
       "│    └─Inception: 2-6                         [1, 256, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-7                  [1, 64, 28, 28]           12,416\n",
       "│    │    └─Sequential: 3-8                   [1, 128, 28, 28]          129,472\n",
       "│    │    └─Sequential: 3-9                   [1, 32, 28, 28]           15,968\n",
       "│    │    └─Sequential: 3-10                  [1, 32, 28, 28]           6,208\n",
       "│    └─Inception: 2-7                         [1, 480, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-11                 [1, 128, 28, 28]          33,024\n",
       "│    │    └─Sequential: 3-12                  [1, 192, 28, 28]          254,592\n",
       "│    │    └─Sequential: 3-13                  [1, 96, 28, 28]           85,248\n",
       "│    │    └─Sequential: 3-14                  [1, 64, 28, 28]           16,512\n",
       "│    └─MaxPool2d: 2-8                         [1, 480, 14, 14]          --\n",
       "├─Sequential: 1-4                             [1, 832, 7, 7]            --\n",
       "│    └─Inception: 2-9                         [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-15                 [1, 192, 14, 14]          92,544\n",
       "│    │    └─Sequential: 3-16                  [1, 208, 14, 14]          226,400\n",
       "│    │    └─Sequential: 3-17                  [1, 48, 14, 14]           27,008\n",
       "│    │    └─Sequential: 3-18                  [1, 64, 14, 14]           30,848\n",
       "│    └─Inception: 2-10                        [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-19                 [1, 160, 14, 14]          82,240\n",
       "│    │    └─Sequential: 3-20                  [1, 224, 14, 14]          283,808\n",
       "│    │    └─Sequential: 3-21                  [1, 64, 14, 14]           50,864\n",
       "│    │    └─Sequential: 3-22                  [1, 64, 14, 14]           32,896\n",
       "│    └─Inception: 2-11                        [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-23                 [1, 128, 14, 14]          65,792\n",
       "│    │    └─Sequential: 3-24                  [1, 256, 14, 14]          361,216\n",
       "│    │    └─Sequential: 3-25                  [1, 64, 14, 14]           50,864\n",
       "│    │    └─Sequential: 3-26                  [1, 64, 14, 14]           32,896\n",
       "│    └─Inception: 2-12                        [1, 528, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-27                 [1, 112, 14, 14]          57,568\n",
       "│    │    └─Sequential: 3-28                  [1, 288, 14, 14]          447,840\n",
       "│    │    └─Sequential: 3-29                  [1, 64, 14, 14]           67,776\n",
       "│    │    └─Sequential: 3-30                  [1, 64, 14, 14]           32,896\n",
       "│    └─Inception: 2-13                        [1, 832, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-31                 [1, 256, 14, 14]          135,680\n",
       "│    │    └─Sequential: 3-32                  [1, 320, 14, 14]          546,240\n",
       "│    │    └─Sequential: 3-33                  [1, 128, 14, 14]          119,616\n",
       "│    │    └─Sequential: 3-34                  [1, 128, 14, 14]          67,840\n",
       "│    └─MaxPool2d: 2-14                        [1, 832, 7, 7]            --\n",
       "├─Sequential: 1-5                             [1, 1024, 1, 1]           --\n",
       "│    └─Inception: 2-15                        [1, 832, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-35                 [1, 256, 7, 7]            213,504\n",
       "│    │    └─Sequential: 3-36                  [1, 320, 7, 7]            594,880\n",
       "│    │    └─Sequential: 3-37                  [1, 128, 7, 7]            129,344\n",
       "│    │    └─Sequential: 3-38                  [1, 128, 7, 7]            106,752\n",
       "│    └─Inception: 2-16                        [1, 1024, 7, 7]           --\n",
       "│    │    └─BasicConv2d: 3-39                 [1, 384, 7, 7]            320,256\n",
       "│    │    └─Sequential: 3-40                  [1, 384, 7, 7]            824,448\n",
       "│    │    └─Sequential: 3-41                  [1, 128, 7, 7]            193,888\n",
       "│    │    └─Sequential: 3-42                  [1, 128, 7, 7]            106,752\n",
       "│    └─AdaptiveAvgPool2d: 2-17                [1, 1024, 1, 1]           --\n",
       "├─Sequential: 1-6                             [1, 1000]                 --\n",
       "│    └─Flatten: 2-18                          [1, 1024]                 --\n",
       "│    └─Dropout: 2-19                          [1, 1024]                 --\n",
       "│    └─Linear: 2-20                           [1, 1000]                 1,025,000\n",
       "===============================================================================================\n",
       "Total params: 7,005,832\n",
       "Trainable params: 7,005,832\n",
       "Non-trainable params: 0\n",
       "Total mult-adds (G): 1.58\n",
       "===============================================================================================\n",
       "Input size (MB): 0.60\n",
       "Forward/backward pass size (MB): 51.63\n",
       "Params size (MB): 28.02\n",
       "Estimated Total Size (MB): 80.25\n",
       "==============================================================================================="
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看模型结构及参数量，input_size表示示例输入数据的维度信息\n",
    "summary(GoogLeNet(), input_size=(1, 3, 224, 224))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0764f2f3",
   "metadata": {},
   "source": [
    "### torchvision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "63f59c0f",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "==========================================================================================\n",
       "Layer (type:depth-idx)                   Output Shape              Param #\n",
       "==========================================================================================\n",
       "GoogLeNet                                [1, 1000]                 6,379,984\n",
       "├─BasicConv2d: 1-1                       [1, 64, 112, 112]         --\n",
       "│    └─Conv2d: 2-1                       [1, 64, 112, 112]         9,408\n",
       "│    └─BatchNorm2d: 2-2                  [1, 64, 112, 112]         128\n",
       "├─MaxPool2d: 1-2                         [1, 64, 56, 56]           --\n",
       "├─BasicConv2d: 1-3                       [1, 64, 56, 56]           --\n",
       "│    └─Conv2d: 2-3                       [1, 64, 56, 56]           4,096\n",
       "│    └─BatchNorm2d: 2-4                  [1, 64, 56, 56]           128\n",
       "├─BasicConv2d: 1-4                       [1, 192, 56, 56]          --\n",
       "│    └─Conv2d: 2-5                       [1, 192, 56, 56]          110,592\n",
       "│    └─BatchNorm2d: 2-6                  [1, 192, 56, 56]          384\n",
       "├─MaxPool2d: 1-5                         [1, 192, 28, 28]          --\n",
       "├─Inception: 1-6                         [1, 256, 28, 28]          --\n",
       "│    └─BasicConv2d: 2-7                  [1, 64, 28, 28]           --\n",
       "│    │    └─Conv2d: 3-1                  [1, 64, 28, 28]           12,288\n",
       "│    │    └─BatchNorm2d: 3-2             [1, 64, 28, 28]           128\n",
       "│    └─Sequential: 2-8                   [1, 128, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-3             [1, 96, 28, 28]           18,624\n",
       "│    │    └─BasicConv2d: 3-4             [1, 128, 28, 28]          110,848\n",
       "│    └─Sequential: 2-9                   [1, 32, 28, 28]           --\n",
       "│    │    └─BasicConv2d: 3-5             [1, 16, 28, 28]           3,104\n",
       "│    │    └─BasicConv2d: 3-6             [1, 32, 28, 28]           4,672\n",
       "│    └─Sequential: 2-10                  [1, 32, 28, 28]           --\n",
       "│    │    └─MaxPool2d: 3-7               [1, 192, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-8             [1, 32, 28, 28]           6,208\n",
       "├─Inception: 1-7                         [1, 480, 28, 28]          --\n",
       "│    └─BasicConv2d: 2-11                 [1, 128, 28, 28]          --\n",
       "│    │    └─Conv2d: 3-9                  [1, 128, 28, 28]          32,768\n",
       "│    │    └─BatchNorm2d: 3-10            [1, 128, 28, 28]          256\n",
       "│    └─Sequential: 2-12                  [1, 192, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-11            [1, 128, 28, 28]          33,024\n",
       "│    │    └─BasicConv2d: 3-12            [1, 192, 28, 28]          221,568\n",
       "│    └─Sequential: 2-13                  [1, 96, 28, 28]           --\n",
       "│    │    └─BasicConv2d: 3-13            [1, 32, 28, 28]           8,256\n",
       "│    │    └─BasicConv2d: 3-14            [1, 96, 28, 28]           27,840\n",
       "│    └─Sequential: 2-14                  [1, 64, 28, 28]           --\n",
       "│    │    └─MaxPool2d: 3-15              [1, 256, 28, 28]          --\n",
       "│    │    └─BasicConv2d: 3-16            [1, 64, 28, 28]           16,512\n",
       "├─MaxPool2d: 1-8                         [1, 480, 14, 14]          --\n",
       "├─Inception: 1-9                         [1, 512, 14, 14]          --\n",
       "│    └─BasicConv2d: 2-15                 [1, 192, 14, 14]          --\n",
       "│    │    └─Conv2d: 3-17                 [1, 192, 14, 14]          92,160\n",
       "│    │    └─BatchNorm2d: 3-18            [1, 192, 14, 14]          384\n",
       "│    └─Sequential: 2-16                  [1, 208, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-19            [1, 96, 14, 14]           46,272\n",
       "│    │    └─BasicConv2d: 3-20            [1, 208, 14, 14]          180,128\n",
       "│    └─Sequential: 2-17                  [1, 48, 14, 14]           --\n",
       "│    │    └─BasicConv2d: 3-21            [1, 16, 14, 14]           7,712\n",
       "│    │    └─BasicConv2d: 3-22            [1, 48, 14, 14]           7,008\n",
       "│    └─Sequential: 2-18                  [1, 64, 14, 14]           --\n",
       "│    │    └─MaxPool2d: 3-23              [1, 480, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-24            [1, 64, 14, 14]           30,848\n",
       "├─Inception: 1-10                        [1, 512, 14, 14]          --\n",
       "│    └─BasicConv2d: 2-19                 [1, 160, 14, 14]          --\n",
       "│    │    └─Conv2d: 3-25                 [1, 160, 14, 14]          81,920\n",
       "│    │    └─BatchNorm2d: 3-26            [1, 160, 14, 14]          320\n",
       "│    └─Sequential: 2-20                  [1, 224, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-27            [1, 112, 14, 14]          57,568\n",
       "│    │    └─BasicConv2d: 3-28            [1, 224, 14, 14]          226,240\n",
       "│    └─Sequential: 2-21                  [1, 64, 14, 14]           --\n",
       "│    │    └─BasicConv2d: 3-29            [1, 24, 14, 14]           12,336\n",
       "│    │    └─BasicConv2d: 3-30            [1, 64, 14, 14]           13,952\n",
       "│    └─Sequential: 2-22                  [1, 64, 14, 14]           --\n",
       "│    │    └─MaxPool2d: 3-31              [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-32            [1, 64, 14, 14]           32,896\n",
       "├─Inception: 1-11                        [1, 512, 14, 14]          --\n",
       "│    └─BasicConv2d: 2-23                 [1, 128, 14, 14]          --\n",
       "│    │    └─Conv2d: 3-33                 [1, 128, 14, 14]          65,536\n",
       "│    │    └─BatchNorm2d: 3-34            [1, 128, 14, 14]          256\n",
       "│    └─Sequential: 2-24                  [1, 256, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-35            [1, 128, 14, 14]          65,792\n",
       "│    │    └─BasicConv2d: 3-36            [1, 256, 14, 14]          295,424\n",
       "│    └─Sequential: 2-25                  [1, 64, 14, 14]           --\n",
       "│    │    └─BasicConv2d: 3-37            [1, 24, 14, 14]           12,336\n",
       "│    │    └─BasicConv2d: 3-38            [1, 64, 14, 14]           13,952\n",
       "│    └─Sequential: 2-26                  [1, 64, 14, 14]           --\n",
       "│    │    └─MaxPool2d: 3-39              [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-40            [1, 64, 14, 14]           32,896\n",
       "├─Inception: 1-12                        [1, 528, 14, 14]          --\n",
       "│    └─BasicConv2d: 2-27                 [1, 112, 14, 14]          --\n",
       "│    │    └─Conv2d: 3-41                 [1, 112, 14, 14]          57,344\n",
       "│    │    └─BatchNorm2d: 3-42            [1, 112, 14, 14]          224\n",
       "│    └─Sequential: 2-28                  [1, 288, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-43            [1, 144, 14, 14]          74,016\n",
       "│    │    └─BasicConv2d: 3-44            [1, 288, 14, 14]          373,824\n",
       "│    └─Sequential: 2-29                  [1, 64, 14, 14]           --\n",
       "│    │    └─BasicConv2d: 3-45            [1, 32, 14, 14]           16,448\n",
       "│    │    └─BasicConv2d: 3-46            [1, 64, 14, 14]           18,560\n",
       "│    └─Sequential: 2-30                  [1, 64, 14, 14]           --\n",
       "│    │    └─MaxPool2d: 3-47              [1, 512, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-48            [1, 64, 14, 14]           32,896\n",
       "├─Inception: 1-13                        [1, 832, 14, 14]          --\n",
       "│    └─BasicConv2d: 2-31                 [1, 256, 14, 14]          --\n",
       "│    │    └─Conv2d: 3-49                 [1, 256, 14, 14]          135,168\n",
       "│    │    └─BatchNorm2d: 3-50            [1, 256, 14, 14]          512\n",
       "│    └─Sequential: 2-32                  [1, 320, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-51            [1, 160, 14, 14]          84,800\n",
       "│    │    └─BasicConv2d: 3-52            [1, 320, 14, 14]          461,440\n",
       "│    └─Sequential: 2-33                  [1, 128, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-53            [1, 32, 14, 14]           16,960\n",
       "│    │    └─BasicConv2d: 3-54            [1, 128, 14, 14]          37,120\n",
       "│    └─Sequential: 2-34                  [1, 128, 14, 14]          --\n",
       "│    │    └─MaxPool2d: 3-55              [1, 528, 14, 14]          --\n",
       "│    │    └─BasicConv2d: 3-56            [1, 128, 14, 14]          67,840\n",
       "├─MaxPool2d: 1-14                        [1, 832, 7, 7]            --\n",
       "├─Inception: 1-15                        [1, 832, 7, 7]            --\n",
       "│    └─BasicConv2d: 2-35                 [1, 256, 7, 7]            --\n",
       "│    │    └─Conv2d: 3-57                 [1, 256, 7, 7]            212,992\n",
       "│    │    └─BatchNorm2d: 3-58            [1, 256, 7, 7]            512\n",
       "│    └─Sequential: 2-36                  [1, 320, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-59            [1, 160, 7, 7]            133,440\n",
       "│    │    └─BasicConv2d: 3-60            [1, 320, 7, 7]            461,440\n",
       "│    └─Sequential: 2-37                  [1, 128, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-61            [1, 32, 7, 7]             26,688\n",
       "│    │    └─BasicConv2d: 3-62            [1, 128, 7, 7]            37,120\n",
       "│    └─Sequential: 2-38                  [1, 128, 7, 7]            --\n",
       "│    │    └─MaxPool2d: 3-63              [1, 832, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-64            [1, 128, 7, 7]            106,752\n",
       "├─Inception: 1-16                        [1, 1024, 7, 7]           --\n",
       "│    └─BasicConv2d: 2-39                 [1, 384, 7, 7]            --\n",
       "│    │    └─Conv2d: 3-65                 [1, 384, 7, 7]            319,488\n",
       "│    │    └─BatchNorm2d: 3-66            [1, 384, 7, 7]            768\n",
       "│    └─Sequential: 2-40                  [1, 384, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-67            [1, 192, 7, 7]            160,128\n",
       "│    │    └─BasicConv2d: 3-68            [1, 384, 7, 7]            664,320\n",
       "│    └─Sequential: 2-41                  [1, 128, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-69            [1, 48, 7, 7]             40,032\n",
       "│    │    └─BasicConv2d: 3-70            [1, 128, 7, 7]            55,552\n",
       "│    └─Sequential: 2-42                  [1, 128, 7, 7]            --\n",
       "│    │    └─MaxPool2d: 3-71              [1, 832, 7, 7]            --\n",
       "│    │    └─BasicConv2d: 3-72            [1, 128, 7, 7]            106,752\n",
       "├─AdaptiveAvgPool2d: 1-17                [1, 1024, 1, 1]           --\n",
       "├─Dropout: 1-18                          [1, 1024]                 --\n",
       "├─Linear: 1-19                           [1, 1000]                 1,025,000\n",
       "==========================================================================================\n",
       "Total params: 13,004,888\n",
       "Trainable params: 13,004,888\n",
       "Non-trainable params: 0\n",
       "Total mult-adds (G): 1.50\n",
       "==========================================================================================\n",
       "Input size (MB): 0.60\n",
       "Forward/backward pass size (MB): 51.63\n",
       "Params size (MB): 26.50\n",
       "Estimated Total Size (MB): 78.73\n",
       "=========================================================================================="
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看torchvision自带的模型结构及参数量\n",
    "from torchvision import models\n",
    "summary(models.googlenet(), input_size=(1, 3, 224, 224))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b74e7ded",
   "metadata": {},
   "source": [
    "### 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "363178b4",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0 Loss: 2.6317677689955694 Acc: 0.020588235294117647\n",
      "Epoch: 10 Loss: 2.395508761994422 Acc: 0.2196078431372549\n",
      "Epoch: 20 Loss: 2.28469708398559 Acc: 0.3627450980392157\n",
      "Epoch: 30 Loss: 2.187414484941042 Acc: 0.47352941176470587\n",
      "Epoch: 40 Loss: 2.1206486332542362 Acc: 0.4980392156862745\n",
      "Epoch: 50 Loss: 2.038020237563471 Acc: 0.5725490196078431\n",
      "Epoch: 60 Loss: 1.9955861645904358 Acc: 0.6039215686274509\n",
      "Epoch: 70 Loss: 1.9263678811756906 Acc: 0.6196078431372549\n",
      "Epoch: 80 Loss: 1.864129295347834 Acc: 0.6470588235294118\n",
      "Epoch: 90 Loss: 1.8199936513438535 Acc: 0.6480392156862745\n",
      "Epoch: 100 Loss: 1.7838434345956953 Acc: 0.6784313725490196\n",
      "Epoch: 110 Loss: 1.7353312610685694 Acc: 0.6764705882352942\n",
      "Epoch: 120 Loss: 1.689477948576808 Acc: 0.6803921568627451\n",
      "Epoch: 130 Loss: 1.6640181441912987 Acc: 0.6980392156862745\n",
      "Epoch: 140 Loss: 1.65304365093976 Acc: 0.6950980392156862\n",
      "Epoch: 150 Loss: 1.6326827163541928 Acc: 0.7019607843137254\n",
      "Epoch: 160 Loss: 1.5843966353393648 Acc: 0.7       \n",
      "Epoch: 170 Loss: 1.531256532385126 Acc: 0.6764705882352942\n",
      "Epoch: 180 Loss: 1.52860433566366 Acc: 0.7098039215686275\n",
      "Epoch: 190 Loss: 1.528751438587525 Acc: 0.7176470588235294\n",
      "100%|██████████| 200/200 [1:17:11<00:00, 23.16s/it]\n"
     ]
    },
    {
     "data": {
      "image/png": "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XuzbmKSTmfy8IZ6/f76GyUPb0L5OkLOLJiJSvmSnw/9FXP3XffYQePgWe/P+/fsXeDxp0iRCQkLYsmULS5cu5ciRI6xatcrR47Ru3bqObV999VUGDRrEK6+84ljWokWLEhd55MiRBWpUAEaPHu34/bHHHuPXX3/lu+++o23btqSmpvLOO+/w/vvvc9999wFQp04dOnbs6Dimxx57jB9//JEBAwYA+df4Kzm8RYnCSHHauuZVIeV5+umnefrpp0tUqNLKxcXGmwNakJKRw6IdRxg6ZSWfDGlN5/qqIRERqWh2797NCy+8wPLly0lKSnLUesTFxbFu3Tpatmx5zqEv1q1bx0MPPXTJZWjdunWBx7m5ubz22mt88803HDx40NEhxNfXhKytW7eSmZlJjx49inw+T09P7r33XiZPnsyAAQNYt24d69evL3TL6HIrURgR8HRzZeLgaB79MoY/tyXy4GermTS0tW7ZiIhcLu4+ppbCGa9bAjfffDORkZF88sknREREYLfbadq0KVlZWY6mC+dyofU2m61QBUBRDVTzQkaeN998k7fffpvx48fTrFkzfH19GTlyJFlZWcV6XTC3aq655hoOHDjA5MmT6dGjB1FRURfc71JooryL4OXuykf3RtOzcShZuXb+/vka1uw75uxiiYiUDzabuV1ytX9KcBvi6NGjbN26leeff54ePXrQqFEjjh8/7ljfvHlz1q1bx7FjRV8bmjdvzh9//HHO569atSrx8fGOxzt37iQ9/cLtaBYvXky/fv249957adGiBbVr1y7Qe7VevXp4e3uf97WbNWtG69at+eSTT/jqq6944IEHLvi6l0ph5CJ5uLnw3t0t6Vy/Kqeycxk6ZZUCiYhIBVGlShWCgoKYOHEiu3bt4s8//2TUqFGO9XfddRdhYWHceuut/PXXX+zZs4fp06ezbNkyAF566SW+/vprXnrpJbZu3crGjRt5/fXXHft3796d999/n5iYGFavXs2wYcNwd3e/YLnq1q3L3LlzWbp0KVu3buXhhx92dDYB8PLyYsyYMTz99NN8/vnn7N69m+XLlzNp0qQCz/Pggw/y2muvkZuby2233Xapb9cFKYxcAk83Vz66txWto6qQmpHDgI+X8/bcHeTk2p1dNBERuYJcXFyYNm0aa9asoWnTpjz55JP897//daz38PDg999/JyQkhD59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o7tydFQVvNxdSM8yPWJ6NApxTBLo4+HGjU3DmBFzkJpBPnx8b+kKIuCkrr3t2rVj6tSp51zv6emJp6emYhYRkfKhc72qJowA95WwVqQ4vNxdaVsriIWne9Sc3SB1zI0NCQ/w4q5ra1DF1+Oyv/6lcsqgZ2vXriU83HldiERERK6mbg3NBHjhAV7c0Dj0irxG3u2gqn6ehdqjhPp78Y9eDalexeeKvPalKnHNyMmTJ9m1a5fjcWxsLOvWrSMwMJAaNWowduxYDh48yOeffw7A+PHjqVmzJk2aNCErK4upU6cyffp0pk+ffvmOQkREpBTrXC+Ytwa0oElEQIHh6y+nO1tHsnb/CW5uHnHFXuNKKXEYWb16Nd26dXM8zmvbcd999/Hpp58SHx9PXFycY31WVhajR4/m4MGDeHt706RJE2bPnk2fPn0uQ/FFRERKP5vNxu2tql/R1wjwdueDu1td0de4UjRrr4iIiFwRmrVXREREygSFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXGqEoeRRYsWcfPNNxMREYHNZmPmzJkX3GfhwoVER0fj5eVF7dq1+eijjy6mrCIiIlIOlTiMpKWl0aJFC95///1ibR8bG0ufPn3o1KkTa9eu5dlnn+Xxxx9n+vTpJS6siIiIlD9uJd2hd+/e9O7du9jbf/TRR9SoUYPx48cD0KhRI1avXs0bb7xB//79S/ryIiIiUs5c8TYjy5Yto2fPngWW9erVi9Wr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erRu3ZqYmBhatWp1tYtaYhf7uSyL53Hy5Mncc889eHl5FVheVs7hua4P4Py/xQp5myY4OBhXV9dCiS4xMbFQOixLHnvsMWbNmsX8+fOpXr36ebcNDw8nKiqKnTt3XqXSXV6+vr40a9aMnTt3OnrVlJfzuW/fPubNm8eDDz543u3K8jkszjkLCwsjKyuL48ePn3ObsiA7O5sBAwYQGxvL3LlzLzgte6tWrXB3dy+T5xUKfy7Ly3lcvHgx27dvv+DfJZTOc3iu60Np+VuskGHEw8OD6OjoQlVoc+fOpUOHDk4q1cWzLIsRI0YwY8YM/vzzT2rVqnXBfY4ePcr+/fsJDw+/CiW8/DIzM9m6dSvh4eGO6tEzz2dWVhYLFy4sk+dzypQphISEcNNNN513u7J8DotzzqKjo3F3dy+wTXx8PJs2bSoz5zUviOzcuZN58+YRFBR0wX02b95MdnZ2mTyvUPhzWR7OI5jayujoaFq0aHHBbUvTObzQ9aHU/C1elmawZdC0adMsd3d3a9KkSdaWLVuskSNHWr6+vtbevXudXbQSe+SRR6yAgABrwYIFVnx8vOMnPT3dsizLSk1NtZ566ilr6dKlVmxsrDV//nyrffv2VrVq1ayUlBQnl754nnrqKWvBggXWnj17rOXLl1t9+/a1/Pz8HOfrtddeswICAqwZM2ZYGzdutO666y4rPDy8zBxfntzcXKtGjRrWmDFjCiwvi+cwNTXVWrt2rbV27VoLsN566y1r7dq1jp4kxTlnw4YNs6pXr27NmzfPiomJsbp37261aNHCysnJcdZhFXC+Y8zOzrZuueUWq3r16ta6desK/G1mZmZalmVZu3btsl555RVr1apVVmxsrDV79myrYcOGVsuWLcvEMRb3c1maz+OFPqeWZVnJycmWj4+P9eGHHxbav7SfwwtdHyyrdPwtVtgwYlmW9cEHH1hRUVGWh4eH1apVqwJdYcsSoMifKVOmWJZlWenp6VbPnj2tqlWrWu7u7laNGjWs++67z4qLi3NuwUtg4MCBVnh4uOXu7m5FRERYt99+u7V582bHervdbr300ktWWFiY5enpaXXu3NnauHGjE0t8cX777TcLsLZv315geVk8h/Pnzy/yc3nfffdZllW8c3bq1ClrxIgRVmBgoOXt7W317du3VB3z+Y4xNjb2nH+b8+fPtyzLsuLi4qzOnTtbgYGBloeHh1WnTh3r8ccft44ePercAzvD+Y6xuJ/L0nweL/Q5tSzL+vjjjy1vb2/rxIkThfYv7efwQtcHyyodf4u204UVERERcYoK2WZERERESg+FEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxKoURERERcSqFEREREXEqhRERERFxqv8HCJCi2c7YRq8AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 0.6950980392156862\n"
     ]
    }
   ],
   "source": [
    "# 导入必要的库\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader\n",
    "from torchvision import datasets, transforms, models\n",
    "from tqdm import *\n",
    "import numpy as np\n",
    "import sys\n",
    "\n",
    "# 设备检测，若未检测到cuda设备则在CPU上运行\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "# 设置随机种子\n",
    "torch.manual_seed(0)\n",
    "\n",
    "# 定义模型、优化器、损失函数\n",
    "model = GoogLeNet(num_classes=102).to(device)\n",
    "optimizer = optim.SGD(model.parameters(), lr=0.002, momentum=0.9)\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "# 设置训练集的数据变换，进行数据增强\n",
    "trainform_train = transforms.Compose([\n",
    "    transforms.RandomRotation(30), # 随机旋转 -30度到30度之间\n",
    "    transforms.RandomResizedCrop((224, 224)), # 随机比例裁剪并进行resize\n",
    "    transforms.RandomHorizontalFlip(p = 0.5), # 随机水平翻转\n",
    "    transforms.RandomVerticalFlip(p = 0.5), # 随机垂直翻转\n",
    "    transforms.ToTensor(),  # 将数据转换为张量\n",
    "    # 对三通道数据进行归一化(均值，标准差)，数值是从ImageNet数据集上的百万张图片中随机抽样计算得到\n",
    "    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "\n",
    "# 设置测试集的数据变换，不进行数据增强，仅使用resize和归一化\n",
    "transform_test = transforms.Compose([\n",
    "    transforms.Resize((224, 224)),  # resize\n",
    "    transforms.ToTensor(),  # 将数据转换为张量\n",
    "    # 对三通道数据进行归一化(均值，标准差)，数值是从ImageNet数据集上的百万张图片中随机抽样计算得到\n",
    "    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "])\n",
    "\n",
    "# 加载训练数据，需要特别注意的是Flowers102数据集，test簇的数据量较多些，所以这里使用\"test\"作为训练集\n",
    "train_dataset = datasets.Flowers102(root='../data/flowers102', split=\"test\", download=True, transform=trainform_train)\n",
    "# 实例化训练数据加载器\n",
    "train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)\n",
    "# 加载测试数据，使用\"train\"作为测试集\n",
    "test_dataset = datasets.Flowers102(root='../data/flowers102', split=\"train\", download=True, transform=transform_test)\n",
    "# 实例化测试数据加载器\n",
    "test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4)\n",
    "\n",
    "# 设置epoch数并开始训练\n",
    "num_epochs = 200  # 设置epoch数\n",
    "loss_history = []  # 创建损失历史记录列表\n",
    "acc_history = []   # 创建准确率历史记录列表\n",
    "\n",
    "# tqdm用于显示进度条并评估任务时间开销\n",
    "for epoch in tqdm(range(num_epochs), file=sys.stdout):\n",
    "    # 记录损失和预测正确数\n",
    "    total_loss = 0\n",
    "    total_correct = 0\n",
    "    \n",
    "    # 批量训练\n",
    "    model.train()\n",
    "    for inputs, labels in train_loader:\n",
    "        # 将数据转移到指定计算资源设备上\n",
    "        inputs = inputs.to(device)\n",
    "        labels = labels.to(device)\n",
    "        \n",
    "        # 预测、损失函数、反向传播\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(inputs)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        # 记录训练集loss\n",
    "        total_loss += loss.item()\n",
    "    \n",
    "    # 测试模型，不计算梯度\n",
    "    model.eval()\n",
    "    with torch.no_grad():\n",
    "        for inputs, labels in test_loader:\n",
    "            # 将数据转移到指定计算资源设备上\n",
    "            inputs = inputs.to(device)\n",
    "            labels = labels.to(device)\n",
    "            \n",
    "            # 预测\n",
    "            outputs = model(inputs)\n",
    "            # 记录测试集预测正确数\n",
    "            total_correct += (outputs.argmax(1) == labels).sum().item()\n",
    "        \n",
    "    # 记录训练集损失和测试集准确率\n",
    "    loss_history.append(np.log10(total_loss))  # 将损失加入损失历史记录列表，由于数值有时较大，这里取对数\n",
    "    acc_history.append(total_correct / len(test_dataset))# 将准确率加入准确率历史记录列表\n",
    "    \n",
    "    # 打印中间值\n",
    "    if epoch % 10 == 0:\n",
    "        tqdm.write(\"Epoch: {0} Loss: {1} Acc: {2}\".format(epoch, loss_history[-1], acc_history[-1]))\n",
    "\n",
    "# 使用Matplotlib绘制损失和准确率的曲线图\n",
    "import matplotlib.pyplot as plt\n",
    "plt.plot(loss_history, label='loss')\n",
    "plt.plot(acc_history, label='accuracy')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "# 输出准确率\n",
    "print(\"Accuracy:\", acc_history[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83b20646",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
